<!--表格组件-->
<template>
  <!--  查询参数  -->
  <div class="params">
    <!--用户自定义查询参数(只有传入的时候才会调用)-->
    <a-form layout="inline" :model="pageParams" autocomplete="off">
      <!--表单项-->
      <BaseFormItem
        v-for="(item, index) in params"
        :key="index"
        v-model:value="pageParams[item.name]"
        :placeholder="item?.placeholder ? item?.placeholder : `请输入${item.label}`"
        v-bind="item"
        @change="searchChange"
      />
      <!--操作按钮-->
      <a-button v-if="params.length > 0" v-auth="'view'" type="primary" @click="searchChange"> 查询 </a-button>
      <a-button v-auth="'add'" type="primary" @click="handlerOperation(0)"> 新增 </a-button>
      <a-button v-if="batchDelete" v-auth="'delete'" type="danger" :loading="loading" @click="handlerDelete">
        批量删除
      </a-button>
      <!--批处理按钮-->
      <template v-if="hasBatchAction">
        <a-button
          v-for="(item, index) in extraBatchAction"
          :key="index"
          :v-auth="item.auth"
          :type="item.type"
          @click="item.action(selectIds)"
        >
          {{ item.name }}
        </a-button>
      </template>
    </a-form>
  </div>
  <!-- 表格 -->
  <a-table
    :bordered="bordered"
    :columns="columns"
    :data-source="tableData"
    :show-header="showHeader"
    :indent-size="indentSize"
    :loading="loading"
    :size="size"
    :children-column-name="childrenColumnName"
    :pagination="pagination"
    :row-selection="hasBatchAction ? rowSelection : null"
    :row-class-name="(_record, index) => (index % 2 === 1 ? 'table-striped' : null)"
    :row-key="(record) => record.id"
    :scroll="{ y: tableScrollY }"
    @change="handleTableChange"
  >
    <!-- 操作列   -->
    <template #bodyCell="{ column, record }">
      <template v-if="column.dataIndex === 'action'">
        <div class="operation">
          <a v-auth="'view'" @click="handlerOperation(1, record)">查看</a>
          <a v-auth="'alter'" @click="handlerOperation(2, record)">修改</a>
          <a v-auth="'delete'" @click="handlerOperation(3, record)">删除</a>
          <!--自定义操作按钮-->
          <a
            v-for="(item, index) in getExtraButton(record)"
            :key="'extraBtn_' + index"
            v-auth="item.auth"
            @click="item.action(record)"
            >{{ item.name }}</a
          >
        </div>
      </template>
    </template>
  </a-table>
  <!-- 查询、新增、修改通用抽屉  -->
  <BaseDrawer v-if="!isCustomUpdate" v-model:visible="drawerProp.visible" v-bind="drawerProp" :width="width">
    <!--查询、新增、修改字段-->
    <a-form
      :label-col="{ style: { width: '100px' } }"
      :model="updateForm"
      autocomplete="off"
      @finish="onFinish"
      @finishFailed="onFinishFailed"
    >
      <!--表单项-->
      <BaseFormItem
        v-for="(item, index) in updateFormItem"
        :key="index"
        v-model:value="updateForm[item.name]"
        v-bind="item"
      />
      <a-form-item v-show="drawerProp.type != 1" :wrapper-col="{ offset: 8, span: 16 }">
        <a-button type="primary" html-type="submit">提交</a-button>
        <a-button type="danger" @click="drawerProp.visible = false">取消</a-button>
      </a-form-item>
    </a-form>
  </BaseDrawer>
</template>

<script lang="ts" setup>
import { defineProps, ref, withDefaults, onMounted, defineExpose, defineEmits, reactive } from 'vue'
import { BaseModel, BasePage } from '../../../entity/base-model'
import { message } from 'ant-design-vue'
export interface UpdateData<T extends BaseModel = any> {
  // 更新类型
  type: number
  // 更新值
  data: T
}
// 事件
const emit = defineEmits<{
  // 操作改变时
  (e: 'operationChange', value: UpdateData): void
}>()

/**
 * 搜索改变时
 * @param val
 */
function searchChange() {
  if (props.searchBefore) {
    props.searchBefore(pageParams.value)
  }
  requestPage()
}
// 自定义动作
export interface CustomAction<T extends BaseModel = any> {
  // 按钮名
  name: string
  // 按钮类型
  type?: string
  // 权限验证名称
  auth?: string
  // 是否显示
  show: (val: T) => boolean
  // 处理方法
  action: (form: T) => void
}
// 扩展批处理动作
export type ExtraBatchAction = Pick<CustomAction, 'name' | 'auth' | 'type' | 'action'>
interface TableProps {
  // 是否展示外边框和列边框
  bordered?: boolean
  // 指定树形结构的列名
  childrenColumnName?: null | string
  // 表格列的配置描述
  columns?: any
  // 数据数组
  dataSource?: any
  // 是否不请求数据
  unLoadData?: boolean
  // 展示树形数据时，每层缩进的宽度，以 px 为单位
  indentSize?: number
  // 是否开启批量操作
  hasBatchAction?: boolean
  // 是否需要批量删除
  batchDelete?: boolean
  // 扩展批量操作
  extraBatchAction?: Array<ExtraBatchAction>
  // 是否显示表头
  showHeader?: boolean
  // 表格大小
  size?: string
  // 表单数据(新增、修改使用)
  form?: any
  // 分页查询参数
  params?: FormItemProps[]
  // 初始搜索参数
  searchParams?: any
  // 搜索前的数据处理方法
  searchBefore?: (params: BaseParam) => void
  // 更新表单参数
  updateParams?: FormItemProps[]
  // 接口名
  api?: string
  // 抽屉名称
  drawerTitle?: string
  // 扩展操作按钮
  extraButton?: Array<CustomAction>
  // 自定义api
  customApi?: {
    // 新增
    add?: Function
    // 修改
    update?: Function
    // 查询
    get?: Function
    // 删除
    delete?: Function
    // 分页
    page?: Function
    // 导入
    import?: Function
    // 导出
    export?: Function
  }
  // 更新之前的操作
  onBeforeUpdate?: (form: BaseType) => void
  // 是否自行控制新增、修改操作
  isCustomUpdate?: boolean
  // 抽屉宽度
  width?: number
}

import BaseFormItem, { SearchValue } from '@/components/base/form-item/BaseFormItem.vue'
import { BaseParam, BaseType, FormItemProps } from '../form-item/props/index'
import { DrawerProps } from '../base-drawer/BaseDrawer.vue'
import { commonAdd, commonUpdate, commonPage, commonDeleteByIds, commonGet } from '../../../api/common/common'
import { ResultModel } from '@/utils/api/RequestApiModel'
// 属性
const props = withDefaults(defineProps<TableProps>(), {
  bordered: true,
  childrenColumnName: 'children',
  columns: [],
  dataSource: [],
  indentSize: 15,
  showHeader: true,
  size: 'small',
  form: {},
  hasBatchAction: false,
  params: () => [],
  updateParams: () => [],
  unLoadData: false,
  drawerTitle: '',
  api: '',
  searchParams: {},
  extraButton: () => [],
  isCustomUpdate: false,
})
// 请求参数
let pageParams = ref<BasePage>({})
// 表格数据
let tableData = ref<Array<FormItemProps>>([])
// 表格初始化请求数据
onMounted(() => {
  // 如果有初始数据，默认不请求
  if (props.unLoadData) {
    // 初始化表格数据
    tableData.value = props.dataSource
    return
  }
  // 否则分页请求数据
  if (props.api == '' && !props?.customApi?.page) {
    throw new Error('表格组件没有配置接口地址或函数')
  }
  // 分页参数如果不存在，则取默认参数
  if (props.searchParams) {
    pageParams.value = { ...pageParams.value, ...props.searchParams }
  }
  if (!pageParams.value.pageSize && !pageParams.value.pageNum) {
    Object.assign(pageParams.value, {
      pageSize: 10,
      pageNum: 1,
    })
  }
  // 初始化请求参数
  if (props.params.length > 0) {
    props.params.map((val) => (pageParams.value[val.name as string] = val.value))
  }
  requestPage()
})
// 分页参数
const pagination = reactive({
  total: 0,
  current: 1,
  pageSize: 10,
  showQuickJumper: true,
  showLessItems: true,
  showSizeChanger: true,
  showTotal: (total: any) => `共 ${total} 条`,
})
// 表格是否加载中
let loading = ref<boolean>(false)
// 表格改变时请求数据
const handleTableChange = (page: { pageSize: number; current: number }, filters: any, sorter: any) => {
  pageParams.value.pageNum = page.current
  pageParams.value.pageSize = page.pageSize
  requestPage()
}
/**
 * 分页数据处理
 * @param res 分页数据
 */
const handlerPage = (res: ResultModel) => {
  const { data } = res
  tableData.value = res.data?.list
  pagination.current = data.pageNum
  pagination.pageSize = data.pageSize
  pagination.total = data.total
  loading.value = false
}
/**
 * 分页请求
 * @param params 额外的参数
 */
const requestPage = async (params?: BaseType) => {
  loading.value = true
  if (props.customApi && props.customApi.page) {
    await props.customApi.page({ ...pageParams.value, ...(params as object) }).then((res: ResultModel) => {
      handlerPage(res)
      return
    })
  }
  await commonPage(props.api + 'page', {
    ...pageParams.value,
    ...props.searchParams,
    ...(params as object),
  }).then((res: ResultModel) => {
    handlerPage(res)
  })
}
/**
 * 获取扩展按钮
 * @param record
 */
function getExtraButton(record: object): Array<CustomAction> {
  return props.extraButton.filter((val) => val.show(record) === true)
}
// 当前选择的数据的id
let selectIds = ref<Array<Number>>([])
// 多选操作
const rowSelection = ref({
  checkStrictly: false,
  // 选择改变时(当前选择行的关键字、选择行的数据)
  onChange: (selectedRowKeys: (string | number)[], selectedRows: any[]) => {
    selectIds.value = selectedRowKeys as unknown as number[]
  },
  // 选择单个(当前选择行数据、是否选择、选择的行数组)
  onSelect: (record: any, selected: boolean, selectedRows: any[]) => {},
  // 选择所有(是否全选、选择的行数据、改变的行数据)
  onSelectAll: (selected: boolean, selectedRows: any[], changeRows: any[]) => {},
})
// 批量删除
const handlerDelete = () => {
  if (selectIds.value.length == 0) {
    return
  }
  if (confirm('你确认要删除选中的' + selectIds.value.length + '条记录吗?')) {
    handlerDeleteRequest()
  }
}
/**
 * 删除数据请求
 * @param id 主键id
 */
const handlerDeleteRequest = (id?: string) => {
  if (props.customApi && props.customApi.delete) {
    props.customApi.delete(id ? id : selectIds.value.join(',')).then((res: any) => {
      requestPage()
    })
  } else {
    commonDeleteByIds(props.api, id ? id : selectIds.value.join(',')).then((res: any) => {
      requestPage()
    })
  }
}

/**
 * 更新操作
 */
const drawerProp = reactive<DrawerProps>({} as DrawerProps)
/**
 * 操作改变
 * @param type 操作类型 0=新增 1=查看 2=修改 3=删除
 * @param data 当前数据
 */
const handlerOperation = async (type: number, data: BaseModel) => {
  // 操作改变时触发
  emit('operationChange', { type, data })
  // 删除操作直接在当前页面处理
  if (type == 3) {
    if (!props.isCustomUpdate && confirm('你确认要删除选中的记录吗?')) {
      handlerDeleteRequest(data.id as string)
    }
  } else {
    // 新增参数值清空
    if (type == 0) {
      // 表单项清空值
      updateFormItem.value.map((val) => {
        val.value = undefined
      })
      // 表单同步清空值
      Object.keys(updateForm).map((val) => (updateForm[val] = undefined))
    } else {
      const { data: info } = await commonGet(props.api, data.id as string)
      Object.assign(updateForm, info)
      updateFormItem.value.forEach((val) => (val.value = updateForm[val.name]))
    }
    // 查看禁用
    updateFormItem.value.map((val) => (val.disabled = type === 1 ? true : false))
    // 其他操作均通过抽屉处理
    const title = ['新增', '查看', '修改']
    drawerProp.id = data?.id ? data?.id : ''
    drawerProp.type = type
    drawerProp.visible = true
    drawerProp.title = title[type] + props.drawerTitle
  }
}
// 更新操作表单
let updateForm = reactive(props.form)
// 更新操作表单项
const updateFormItem = ref<FormItemProps[]>(props.updateParams)

// 更新数据
const onFinish = async (values: any) => {
  if (props.onBeforeUpdate) {
    props.onBeforeUpdate(updateForm)
  }
  // 新增
  if (drawerProp.type == 0) {
    props.customApi && props.customApi.add
      ? await props.customApi.add(updateForm).then((res: any) => {
          // 重新请求数据
          requestPage()
        })
      : await commonAdd(props.api, updateForm).then((res: any) => {
          // 重新请求数据
          requestPage()
        })
  }
  // 修改
  if (drawerProp.type == 2) {
    props.customApi && props.customApi.update
      ? await props.customApi.update(updateForm).then((res: any) => {
          // 重新请求数据
          requestPage()
        })
      : await commonUpdate(props.api, updateForm).then((res: any) => {
          // 重新请求数据
          requestPage()
        })
  }
  // 关闭抽屉
  drawerProp.visible = false
}
// 表单更新失败
const onFinishFailed = (err: any) => {
  message.error(
    err.errorFields
      .map((val: any) => val.errors)
      .flat(1)
      .join(','),
  )
}

// 暴露方法或属性给父组件
defineExpose({
  // 分页请求方法
  requestPage,
})
// 表格可滚动高度
const tableScrollY = ref<number>(0)
onMounted(() => {
  const totalH = document.getElementsByTagName('body')[0].clientHeight
  const headerH = document.getElementsByClassName('ant-layout-header')[0].clientHeight
  const breadH = document.getElementsByClassName('ant-breadcrumb')[0].clientHeight
  const paramH = document.getElementsByClassName('params')[0].clientHeight
  tableScrollY.value = totalH - (headerH + breadH + paramH + 190)
  // console.log(tableScrollY.value)
})
</script>

<style scoped lang="scss">
@import '@/assets/scss/mixin.scss';
:deep(.table-striped) td {
  background-color: #fafafa;
}
:deep(.ant-table-content) {
  height: 100% !important;
  overflow: auto;
}
.params {
  @include flexMixer(flex-start, flex-start);
  width: 100%;
  overflow-x: auto;
  vertical-align: center;
  :deep(.ant-form-item) {
    margin-bottom: 5px;
  }
  :deep(.ant-input) {
    min-width: 90px;
  }
  :deep(.ant-select-show-search) {
    min-width: 90px;
  }
  :deep(.ant-btn) {
    margin-top: 0px !important;
  }
}
a {
  color: $primary-color !important;
}
:deep(.ant-btn) {
  margin: 10px 5px !important;
}
:deep(.ant-pagination-item-container) {
  margin-top: -23px !important;
}
</style>
